`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2022/03/17 09:43:41
// Design Name: 
// Module Name: MC_RV32Core
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////
`include "Parameters.v"
import Params::*;

module MC_RV32Core(
input logic clk,
input logic rst_n
    );
//状态机
typedef enum bit [2:0] { 
    IDLE,
    IF,ID,EXE,MEM,WB
} State;
State cur_state,next_state;              //状态机
//ID
logic [4:0] RS1;              //第一个源操作数的寄存器编号
logic [4:0] RS2;              //第二个源操作数的寄存器编号
logic [4:0] RD;               //目的寄存器编号
logic [31:0] Imm;             //解析得到的立即数
logic [6:0] Op;               //操作码
logic [2:0] Fn3;              //功能字段
logic [6:0] Fn7;              //功能字段
logic Jal;                    //Jal指令
logic Jalr;                   //Jalr指令
LoadType RegWrite;         //数据选取
logic MemToReg;               //写寄存器的数据是否来自MEM
logic [3:0] MemWrite;         //写数据存储器
logic LoadNpc;                //是否计算PCF+4
BType BranchType;       //条件分支指令类型
AluOp AluControl;       //ALU控制信号
logic AluSrc1;                //第一个源操作数来源
logic [1:0] AluSrc2;          //第二个源操作数来源
Type ImmType;          //立即数类型
logic [31:0] RegOut1;         //寄存器读出值
logic [31:0] RegOut2;         //寄存器读出值
logic [31:0] JalT;            //Jal跳转地址
//IF
logic [31:0] PCF;             //当前PC地址，根据PCF的值读取指令
logic [31:0]Instr;            //读出的指令
logic [31:0] NPC;             //下一条指令的地址
//EXE
logic [31:0] Operand1;              //ALU源操作数1
logic [31:0] Operand2;              //ALU源操作数2
logic [31:0] AluOut;                //ALU输出
logic [31:0] BrT;                   //条件分支跳转地址
logic [31:0] JalrT;                 //Jalr跳转地址
//MEM
logic [31:0] StoreData;             //要写入存储器的数据
logic [31:0] DM_RD;                 //数据存储器读出数据
logic [31:0] Result;                //要写入寄存器堆的数据，或来自Alu,或来自PCF+4，由LoadNpc决定
//WB
logic [31:0] RegWriteData;          //要写入寄存器的数据，或来自于DMEM，或来自于其他
logic [31:0] DM_RD_EXT;             //字节选择后的DMEM读出数据
logic RegWriteEn;                   //是否写寄存器堆
//解析Instr的各个字段,包括RS1,RS2,RD,Op,Fn3,Fn7
assign RS1=Instr[19:15];
assign RS2=Instr[24:20];
assign RD=Instr[11:7];
assign Op=Instr[6:0];
assign Fn3=Instr[14:12];
assign Fn7=Instr[31:25];
//RegWriteEn,寄存器写使能信号
//是否写寄存器堆,仅在WB阶段写    
always_comb 
begin
    if(cur_state==WB)                     //仅在WB阶段写寄存器
    begin
        if(RegWrite==NOREGWRITE)
             RegWriteEn=0;
        else
             RegWriteEn=1;
    end
    else
        RegWriteEn=0;
end    
//PCF
always_ff@(posedge clk,negedge rst_n)
if(~rst_n)
    PCF<=0;
else if(cur_state==WB)                                //地址均在WB阶段更新
    PCF<=NPC;
//Operand1,Operand2
always_comb
begin
    if(AluSrc1==1'b1)
        Operand1=PCF;
    else
        Operand1=RegOut1;
end
always_comb
begin
    if(AluSrc2==2'b00)                       //移位指令
        Operand2=RS2;
    else if(AluSrc2==2'b01)                  //R型和B型指令
        Operand2=RegOut2;
    else                                     //其他，来自立即数
        Operand2=Imm; 
end
//StoreData
assign StoreData=RegOut2;                     //仅在指令为S型指令时有效，其值为Reg[rs2]
//Result
always_comb
begin
    if(LoadNpc)                            //jal,jalr指令,将PCF+4写入Reg[rd]
        Result=PCF+4;
    else
        Result=AluOut;
end
//RegWriteData
always_comb
begin
    if(MemToReg)
         RegWriteData=DM_RD_EXT;
    else
         RegWriteData=Result;
end
//JalT,JalrT,BrT
assign JalT=Imm+PCF;               //加法部件计算得到
assign JalrT=AluOut;               //ALU计算得到
assign BrT=Imm+PCF;                //加法部件计算得到
//三段式状态机
always_ff@(posedge clk,negedge rst_n)
if(~rst_n)
    cur_state<=IDLE;
else
    cur_state<=next_state;
//
always_comb begin
    case(cur_state)
        IDLE:if(~rst_n)
                 next_state=IDLE;
             else
                 next_state=IF;
        IF:next_state=ID;
        ID:next_state=EXE;
        EXE:next_state=MEM;
        MEM:next_state=WB;
        WB:next_state=IF;
        default:next_state=IDLE;
    endcase
end

//模块例化
//控制单元
ControlUnit U1(
.Op(Op),
.Fn3(Fn3),
.Fn7(Fn7),
.JalD(Jal),                      //是否为Jal指令
.JalrD(Jalr),                     //是否是Jalr指令
.RegWriteD(RegWrite),           //LOAD指令字节选取(LW,LH,LB,...)
.MemToRegD(MemToReg),                 //写入寄存器的值是否来自MEM
.MemWriteD(MemWrite),           //写MEM时字节选中信号，S类型指令
.LoadNpcD(LoadNpc),                  //MEM阶段是否计算PC+4,用于Jal和Jalr指令
.BranchTypeD(BranchType),          //分支指令类型，BEQ,BNE,...
.AluControlD(AluControl),          //EXE阶段ALU控制信号，进行何种运算
.AluSrc2D(AluSrc2),             //第二个源操作数的来源
.AluSrc1D(AluSrc1),                   //第一个源操作数的来源，rs1或者pc
.ImmType(ImmType)               //立即数类型，不同类型解析不同
    );
//ALU
ALU U2(
.Operand1(Operand1),
.Operand2(Operand2),
.AluControl(AluControl),
.AluOut(AluOut)
    );
//BranchUnit
BranchUnit U3(
.Reg1(Operand1),               //第一个源操作数
.Reg2(Operand2),               //第二个源操作数
.BrType(BranchType),              //条件分支指令类型
.BranchE(Branch)                   //是否跳转
    );
//数据存储器
DataMem U4(
.clk(clk),
.A(AluOut),                     //地址
.WD(StoreData),                 //要写入的数据
.WE(MemWrite),                          //写使能（一个字节一位） 
.RD(DM_RD)                      //读出的数据
);
//数据选取单元
Data_Ext U5(
.LoadByteSelect(AluOut[1:0]),
.RegWrite(RegWrite),
.In(DM_RD),
.Out(DM_RD_EXT)
    );
//立即数单元
ImmUnit U6(
.ImmType(ImmType),               //指令中立即数的类型
.In(Instr),                      //输入指令
.Out(Imm)                        //输出解析得到的立即数
    );
//指令存储器
InstrMem U7(
.InstrAddr(PCF),                //指令地址，4字节对齐
.Instr(Instr)                    //读出的指令
    );
//PC生成单元
NPC_Generator U8(
.Jal(Jal),
.Jalr(Jalr),
.Branch(Branch),
.JalT(JalT),
.JalrT(JalrT),
.BrT(BrT),
.PC(PCF),
.NPC(NPC)
);
//寄存器堆
RegisterFile U9(
.clk(clk),
.RS1(RS1),                 //要读的寄存器编号
.RS2(RS2),                 //要读的寄存器编号
.RegOut1(RegOut1),           //读出内容：Reg[rs1]
.RegOut2(RegOut2),           //读出内容：Reg[rs2]
.WD(RegWriteData),                 //要写入寄存器的数据
.RD(RD),                  //写寄存器编号
.WE(RegWriteEn)                         //写使能
);
endmodule
